//
// Created by 17775 on 2024/6/24.
//
#include <winsock2.h>
#include <IPHlpApi.h>
#include <cstdio>
#include <cstdlib>
#include <atltypes.h>
#include "LiveMode.h"

#pragma comment(lib, "IPHlpApi.lib")
#pragma comment(lib, "ws2_32.lib")

LiveMode::LiveMode() : liveMode2DView(new LiveMode_2D_View()),
                       rayCiDataReceiveQueueObserver(new RayCiDataReceiveQueueObserver()),
                       powerProfileDataReceiveQueueObserver(new PowerProfileDataReceiveQueueObserver()){
    rayCiDataReceiveQueueObserver->addListener(liveMode2DView);
}


LiveMode::~LiveMode() {
    getNewFrameThreadRunning.store(false, std::memory_order_release);
    getNewPowerDataThreadRunning.store(false, std::memory_order_release);
    queryParameterThreadRunning.store(false, std::memory_order_release);
    if (m_workerThread.joinable()) {
        m_workerThread.join();
    }
    if (m_powerProfileThread.joinable()) {
        m_powerProfileThread.join();
    }
    if (m_queryThread.joinable()) {
        m_queryThread.join();
    }
    rayCiDataReceiveQueueObserver->interrupt();
    powerProfileDataReceiveQueueObserver->interrupt();
    delete rayCiDataReceiveQueueObserver;
    delete powerProfileDataReceiveQueueObserver;
    delete liveMode2DView;
}

void LiveMode::close(bool t) {
    RayCi_closeAllDocuments(t);
}
bool LiveMode::Connect() {
    unsigned short nClientPorts[] = {8000, 8080, 8008, 80};

    _strIP = "127.0.0.1";
    const char *szAddr = _strIP.data();

    auto nClientPort = nClientPorts[0];
    for (const unsigned short i: nClientPorts) {
        if (IsPortAvailable(i)) {
            nClientPort = i;
            break;
        }
    }

    _strPort = "8082";

    if (RayCi_connectA(szAddr, stoi(_strPort), nClientPort)) {
        // register callback
        RayCi_Callback_register_onExit(&CallbackRayCi_onExit, this, &m_nIdCallback_RayCiClosed);
#ifdef USE_DONGLE        // only available with Standard and Pro edition
        RayCi_Callback_register_License_onDongleRemoved( &CMainFrame::CallbackRayCi_onDongleRemoved, this, &m_nIdCallback_DongleRemoved );
#endif
        QueryCameraList();
        ConnectionToLiveMode();
        if (!m_vecCameras.empty()) {
            RayCi_Windows_GlobalSettings_hide();
            //RayCi_Windows_Main_hide();
            RayCi_Windows_Messages_hide();
            bool bValid = false;

            RayCi_LiveMode_Background_isValid(_nDocIdLive, &bValid);
            bool dataAvailable = false;
            RayCi_LiveMode_Background_isLoadableDataAvailable(_nDocIdLive, &dataAvailable);
            if (dataAvailable) {
                bool canLoad = false;
                RayCi_LiveMode_Background_canLoad(_nDocIdLive, &canLoad);
                if (canLoad) {
                    emit InitBackGroundButton(true,false,false);
                }
            }
            else {
                bool canSave = false;
                RayCi_LiveMode_Background_canSave(_nDocIdLive, &canSave);
                if (canSave) {
                    emit InitBackGroundButton(false,true,false);
                }
            }
            RayCi_LiveMode_Background_setApply(_nDocIdLive, true);
        }
        RayCi_LiveMode_Settings_Lut_setAutoContrast(_nDocIdLive, true);
        RayCi_LiveMode_AOI_Adjustment_setActive(_nDocIdLive, true);
        return true;
    } else {
        RayCi_disconnect();
    }
    return false;
}

void LiveMode::DisConnect() {
    CloseLiveMode();
    m_vecCameras.clear();
    // unregister callback
    if (m_nIdCallback_RayCiClosed != 0) {
        RayCi_Callback_unregisterById(m_nIdCallback_RayCiClosed);
        m_nIdCallback_RayCiClosed = 0;
    }
#ifdef USE_DONGLE
    if ( m_nIdCallback_DongleRemoved!=0 )
    {
        RayCi_Callback_unregisterById( m_nIdCallback_DongleRemoved );
        m_nIdCallback_DongleRemoved = 0;
    }
#endif
    RayCi_disconnect();
}

void LiveMode::CallbackRayCi_onExit(IN void *pContext) {
    if (auto *pThis = static_cast<LiveMode *>(pContext)) {
        emit pThis->RayCi_Closed();
    }
}

void LiveMode::RayCi_Closed() {

}

void LiveMode::updateSettings() {
    RayCi_LiveMode_Analysis_Settings_isEllipse(_nDocIdLive, &isEllipse);
    RayCi_LiveMode_Analysis_Settings_isEllipseAvailable(_nDocIdLive, &bEllipseAvailable);
    RayCi_LiveMode_Analysis_Settings_isFractionAvailable(_nDocIdLive, &bFractionAvailable);
    RayCi_LiveMode_Analysis_Settings_isRectangle(_nDocIdLive, &bRectangle);
    RayCi_LiveMode_Analysis_Settings_isRectangleAvailable(_nDocIdLive, &bRectangleAvailable);
    RayCi_LiveMode_Analysis_Settings_isSigmaEnvAvailable(_nDocIdLive, &bSigmaEnvAvailable);
    RayCi_LiveMode_Analysis_Settings_isSlope(_nDocIdLive, &bSlope);
    RayCi_LiveMode_Analysis_Settings_isSlopeAvailable(_nDocIdLive, &bSlopeAvailable);
    RayCi_LiveMode_Analysis_Settings_isThresholdAvailable(_nDocIdLive, &bThresholdAvailable);
    RayCi_LiveMode_Analysis_Settings_getSigmaEnv(_nDocIdLive, &dSigmaEnv);
    RayCi_LiveMode_Analysis_Settings_getThreshold(_nDocIdLive, &dThreshold);
    RayCi_LiveMode_Analysis_Settings_setBeamCoordinates(_nDocIdLive, bBeamCoordinates);
    RayCi_LiveMode_Analysis_Settings_setEllipse(_nDocIdLive);
    RayCi_LiveMode_Analysis_Settings_setFraction(_nDocIdLive, dFraction);
    RayCi_LiveMode_Analysis_Settings_setRectangle(_nDocIdLive);
    RayCi_LiveMode_Analysis_Settings_setSigmaEnv(_nDocIdLive, dSigmaEnv);
    RayCi_LiveMode_Analysis_Settings_setSlope(_nDocIdLive, &bSlope);
    RayCi_LiveMode_Analysis_Settings_setThreshold(_nDocIdLive, dThreshold);
}

void LiveMode::isMethodAvailable(MethodsType method) {
//    RayCi_LiveMode_Analysis_Settings_getMethod();
    RayCi_LiveMode_Analysis_Settings_isMethodAvailable(_nDocIdLive, static_cast<int32_t>(method), &bMethodAvailable);
}

void LiveMode::getPowerData() {
    m_powerProfileThread = std::thread(&LiveMode::getPowerProfileData, this);
}

void LiveMode::getPowerProfileData() {
    while (getNewPowerDataThreadRunning.load(std::memory_order_acquire)) {
        RayCi_LiveMode_PowerProfile_isValid(_nDocIdLive, &powerProfileIsValid);
        if (powerProfileIsValid) {
            RayCi_LiveMode_PowerProfile_getDataLength(_nDocIdLive, &powerProfileDataLength);
            if (powerProfileDataLength != 0) {
                std::vector<float> vecDiameter(powerProfileDataLength);
                std::vector<float> vecPower(powerProfileDataLength);
                RayCi_LiveMode_PowerProfile_getDataAsBinary(
                        _nDocIdLive,
                        vecDiameter.data(),
                        &powerProfileDataLength,
                        vecPower.data(),
                        &powerProfileDataLength
                );
                bool bValid = false;
                RayCi_LiveMode_Analysis_Statistic_Power_isValid(
                        _nDocIdLive, 0, false, &bValid
                );
                if (bValid) {
                    double dPower = 0;
                    RayCi_LiveMode_Analysis_Statistic_Power_getPower(
                            _nDocIdLive, 0, false, &dPower
                    );
                    if (dPower != 0 && dPower >= 1) {
                        std::transform(
                                vecPower.begin(),
                                vecPower.end(),
                                vecPower.begin(),
                                [dPower](float val) { return val / dPower * 100; }
                        );
                    }
                }
                auto powerData = std::make_shared<PowerProfileData<std::vector<float>>>(vecDiameter, vecPower);
                powerProfileDataReceiveQueueObserver->put(powerData);
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}


void LiveMode::stopNewPowerProfileThread() {
    getNewPowerDataThreadRunning.store(false, std::memory_order_release);
    if (m_powerProfileThread.joinable()) {
        m_powerProfileThread.join();
    }
}

void LiveMode::addPowerProfileDataListener(PowerProfileDataReceiveListener *listener) const {
    powerProfileDataReceiveQueueObserver->addListener(listener);
}

void LiveMode::getActualLut(BYTE LUT[256][4], const int32_t nBits) const {
    const int32_t nIdDoc = _nDocIdLive;
    uint32_t szActualLut_buffer[256];
    int nBufferSize = sizeof(szActualLut_buffer);
    RayCi_LiveMode_Settings_Lut_getActualLut(nIdDoc,  nBits, szActualLut_buffer, &nBufferSize);
    assert(nBufferSize == 256 || nBufferSize == 1024);
    for (int i = 0; i < 256; ++i) {
        const uint32_t argb = szActualLut_buffer[i];
        LUT[i][0] = (argb >> 16) & 0xFF; // Red
        LUT[i][1] = (argb >> 8) & 0xFF; // Green
        LUT[i][2] = (argb >> 0) & 0xFF; // Blue
        LUT[i][3] = (argb >> 24) & 0xFF; // Alpha
    }
}

void LiveMode::getBaseLut(BYTE LUT[256][4]) const {
    const int32_t nIdDoc = _nDocIdLive;
    uint32_t szBaseLut_buffer[256];
    int nBufferSize = sizeof(szBaseLut_buffer);
    RayCi_LiveMode_Settings_Lut_getBaseLut(nIdDoc, szBaseLut_buffer, &nBufferSize);
    assert(nBufferSize == 256);
    for (int i = 0; i < 256; ++i) {
        const uint32_t argb = szBaseLut_buffer[i];
        LUT[i][0] = (argb >> 16) & 0xFF; // Red
        LUT[i][1] = (argb >> 8) & 0xFF; // Green
        LUT[i][2] = (argb >> 0) & 0xFF; // Blue
        LUT[i][3] = (argb >> 24) & 0xFF; // Alpha
    }
}

void LiveMode::setLutThreshold(const double dMin, const double dMax) const {
    RayCi_LiveMode_Settings_Lut_setThreshold(_nDocIdLive, dMin, dMax);
}

void LiveMode::setAutoContrastAoiMask(const int nAoiMask) const {
    RayCi_LiveMode_Settings_Lut_setAutoContrastAoiMask(_nDocIdLive, nAoiMask);
}

QString LiveMode::getLutPathName() const {
    char szPathName_buffer[80];
    int nSize = sizeof(szPathName_buffer);
    RayCi_LiveMode_Settings_Lut_getPathNameA(_nDocIdLive, szPathName_buffer, &nSize);
    return QString::fromUtf8(szPathName_buffer);
}

double LiveMode::getPositionX() const {
    double dPositionX = 0;
    RayCi_LiveMode_Settings_CoordSystem_UserDefined_getPositionX(_nDocIdLive, &dPositionX);
    return dPositionX;
}

double LiveMode::getPositionY() const {
    double dPositionY = 0;
    RayCi_LiveMode_Settings_CoordSystem_UserDefined_getPositionX(_nDocIdLive, &dPositionY);
    return dPositionY;
}

int LiveMode::getHorizontalOri() const {
    int dOrientation = 0;
    RayCi_LiveMode_Settings_CoordSystem_Horizontal_getOrientation(_nDocIdLive, &dOrientation);
    return dOrientation;
}

int LiveMode::getVerticalOri() const {
    int dOrientation = 0;
    RayCi_LiveMode_Settings_CoordSystem_Vertical_getOrientation(_nDocIdLive, &dOrientation);
    return dOrientation;
}

// LiveMode_2D_View_Setting AOI
void LiveMode::setAOIActive(const bool active) const {
    RayCi_LiveMode_AOI_Adjustment_setActive(_nDocIdLive, active);
}
void LiveMode::setAOIAdjustmentMethod(const int iMethod) const {
    bool bAvailable = false;
    RayCi_LiveMode_AOI_Adjustment_isAvailable(_nDocIdLive, &bAvailable);
    if(bAvailable) {
        RayCi_LiveMode_AOI_Adjustment_setMethod(_nDocIdLive, iMethod);
    }
}
void LiveMode::setAOIShape(int shape) const {
    RayCi_LiveMode_AOI_setShape(_nDocIdLive, shape);
}
bool LiveMode::getAutoStatus() const {
    bool bAutoContrast = false;
    RayCi_LiveMode_Settings_Lut_isAutoContrast(_nDocIdLive, &bAutoContrast);
    return bAutoContrast;
}

// LiveMode AOI
AOI LiveMode::getPositionSize() const {
    AOI aoi;
    RayCi_LiveMode_AOI_getPositionX_px(_nDocIdLive, &aoi.dPositionX_px);
    RayCi_LiveMode_AOI_getPositionY_px(_nDocIdLive, &aoi.dPositionY_px);
    RayCi_LiveMode_AOI_getMajor_px(_nDocIdLive, &aoi.dMajor_px);
    RayCi_LiveMode_AOI_getMinor_px(_nDocIdLive, &aoi.dMinor_px);
    RayCi_LiveMode_AOI_getAzimuthAngle_px(_nDocIdLive, &aoi.dAzimuthAngle_px);
    RayCi_LiveMode_AOI_getShape(_nDocIdLive, &aoi.nShape);
    RayCi_LiveMode_AOI_Adjustment_getMethod(_nDocIdLive, &aoi.nMethod);
    RayCi_LiveMode_AOI_Adjustment_isActive(_nDocIdLive, &aoi.bActive);
    return aoi;
}
void LiveMode::setPosition_px(double dPosX, double dPosY) const {
    RayCi_LiveMode_AOI_setPosition_px(_nDocIdLive, dPosX, dPosY);
}
double LiveMode::getAzimuthAngle() const {
    double _dPhi = 0;
    RayCi_LiveMode_AOI_getAzimuthAngle_px(_nDocIdLive, &_dPhi);
    return _dPhi;
}
void LiveMode::setAOISize(double dMajor, double dMinor, double dPhi) const {
    RayCi_LiveMode_AOI_setSize_px(_nDocIdLive, dMajor, dMinor, dPhi);
}
void LiveMode::setHorizontalReference(bool isXImageOrAOI, int index) const {
    isXImageOrAOI ? RayCi_LiveMode_Settings_CoordSystem_Horizontal_setReference(_nDocIdLive, index) : RayCi_LiveMode_Settings_CoordSystem_Vertical_setReference(_nDocIdLive, index);
}
void LiveMode::setHorizontalOrigin(bool isLeftOrTop, int index) const {
    isLeftOrTop ? RayCi_LiveMode_Settings_CoordSystem_Horizontal_setOrigin(_nDocIdLive, index - 1) : RayCi_LiveMode_Settings_CoordSystem_Vertical_setOrigin(_nDocIdLive, index -1);
}
double LiveMode::getUserDefinedPositionX() const {
    double dPositionX = 0;
    RayCi_LiveMode_Settings_CoordSystem_UserDefined_getPositionX(_nDocIdLive, &dPositionX);
    return dPositionX;
}
double LiveMode::getUserDefinedPositionY() const {
    double dPositionY = 0;
    RayCi_LiveMode_Settings_CoordSystem_UserDefined_getPositionY(_nDocIdLive, &dPositionY);
    return dPositionY;
}
void LiveMode::setCoordSystemMode(int iMode) {
    switch (iMode) {
        case 0: {
            RayCi_LiveMode_Settings_CoordSystem_setDefault(_nDocIdLive);
            break;
        }
        case 1: {
            RayCi_LiveMode_Settings_CoordSystem_setAutomatic(_nDocIdLive);
            break;
        }
        case 2: {
            RayCi_LiveMode_Settings_CoordSystem_setUserDefined(_nDocIdLive);
            break;
        }
    }
}
void LiveMode::setUserDefined(double dxPos, double dyPos) const {
    RayCi_LiveMode_Settings_CoordSystem_UserDefined_setPosition(_nDocIdLive, dxPos, dyPos);
}

void LiveMode::sigmaIsChecked(const int sigmaIndex) const {
    constexpr double sigmaValues[] = { 1.414, 2.0, 2.828, 3.0 };
    const int safeIndex = (sigmaIndex >= 0 && sigmaIndex < std::size(sigmaValues)) ? sigmaIndex : 3;
    RayCi_LiveMode_Analysis_Settings_setSigmaEnv(_nDocIdLive, sigmaValues[safeIndex]);
}

void LiveMode::thresholdIsChecked(const int thresholdIndex) const {
    constexpr double thresholdValues[] = { 90, 50, 10, 36.79, 13.53 };
    const int safeIndex = (thresholdIndex >= 0 && thresholdIndex < std::size(thresholdValues)) ? thresholdIndex : 4;
    RayCi_LiveMode_Analysis_Settings_setThreshold(_nDocIdLive, thresholdValues[safeIndex]);
}

void LiveMode::powerFractionIsChecked(const int fractionIndex) const {
    constexpr double fractionValues[] = { 63.21, 86.47, 98.17, 98.89 };
    const int safeIndex = (fractionIndex >= 0 && fractionIndex < std::size(fractionValues)) ? fractionIndex : 3;
    RayCi_LiveMode_Analysis_Settings_setFraction(_nDocIdLive, fractionValues[safeIndex]);
}

void LiveMode::thresholdValueChanged(MethodsType methods, const int index) const {
    constexpr double thresholdValues[] = { 90, 50, 10, 36.79, 13.53 };
    const int safeIndex = (index >= 0 && index < std::size(thresholdValues)) ? index : 4;
    RayCi_LiveMode_Analysis_Settings_setThreshold(_nDocIdLive, thresholdValues[safeIndex]);
}

void LiveMode::changeSigmaEnvironment(MethodsType methods, const int index) const {
    constexpr double sigmaValues[] = { 1.414, 2.0, 2.828, 3.0 };
    const int safeIndex = (index >= 0 && index < std::size(sigmaValues)) ? index : 3;
    RayCi_LiveMode_Analysis_Settings_setSigmaEnv(_nDocIdLive, sigmaValues[safeIndex]);
}

void LiveMode::changeFraction(MethodsType methods, const int index) const {
    constexpr double fractionValues[] = { 63.21, 86.47, 98.17, 98.89 };
    const int safeIndex = (index >= 0 && index < std::size(fractionValues)) ? index : 3;
    RayCi_LiveMode_Analysis_Settings_setFraction(_nDocIdLive, fractionValues[safeIndex]);
}

void LiveMode::changeSlope(const MethodsType method, const bool slopeIsChecked) const {
    switch (method) {
        case MethodsType::Plateau: {
            RayCi_LiveMode_Analysis_Settings_setSlope(_nDocIdLive,slopeIsChecked);
            break;
        }
        default: {
            RayCi_LiveMode_Analysis_Settings_setBeamCoordinates(_nDocIdLive,slopeIsChecked);
            break;
        }
    }
}

void LiveMode::queryAOIParameter(const MethodsType method) {
    constexpr int methodIds[] = {
        0, // MethodsType::None 无操作
        1,  // MethodsType::Threshold
        2,  // MethodsType::_2ndMoments
        3,  // MethodsType::FitGauss
        4,  // MethodsType::FitSuperGauss
        5,  // MethodsType::Plateau
        6,  // MethodsType::SimpleGeometry
        7,  // MethodsType::Area
        8,  // MethodsType::KnifeEdge
        9,  // MethodsType::MovingSlit
        10  // MethodsType::Dropdown
    };

    const int methodIndex = static_cast<int>(method);
    if (methodIndex <= 0 || methodIndex >= std::size(methodIds)) {
        return;
    }
    const int methodId = methodIds[methodIndex];
    RayCi_LiveMode_Analysis_Settings_isMethodAvailable(_nDocIdLive, methodId, &bMethodAvailable);
    if (bMethodAvailable) {
        RayCi_LiveMode_Analysis_Settings_setMethod(_nDocIdLive, methodId);
    }
}

bool LiveMode::setSpatialUnit(const QString &unit) const {
    const auto byteArray = unit.toUtf8();
    const auto spUnit = byteArray.constData();
    // const auto ok = RayCi_LiveMode_Settings_Units_Spatial_setUnitA(_nDocIdLive, spUnit);
    return RayCi_LiveMode_Settings_Units_Spatial_setUnitA(_nDocIdLive, spUnit);
}

QString LiveMode::getSpatialUnit() const {
    char szUnit_buffer[10];
    int nSize = sizeof(szUnit_buffer);
    RayCi_LiveMode_Settings_Units_Spatial_getUnitA(_nDocIdLive, szUnit_buffer, &nSize);
    return QString::fromUtf8(szUnit_buffer);
}

void LiveMode::setSpatialUnitManually(const bool bSpatialManually, const double dSizeX, const double dSizeY) const {
    RayCi_LiveMode_Settings_Units_Spatial_setManually(_nDocIdLive, bSpatialManually);
    if (bSpatialManually) {
        RayCi_LiveMode_Settings_Units_Spatial_setPixelSize(_nDocIdLive, dSizeX, dSizeY);
    }
    else {
            double pixelSizeX, pixelSizeY;
            // QString camName = cameraName;

            // if (camName.contains("1201")) {
            //     if (camName.contains("Nano")) {
            //         emit pixelSizeInfo(5.3, 5.3);
            //     } else {
            //         emit pixelSizeInfo(5.2, 5.2);
            //     }
            // } else if (camName.contains("1202")) {
            //     emit pixelSizeInfo(5.3, 5.3);
            // } else if (camName.contains("1203")) {
            //     emit pixelSizeInfo(4.5, 4.5);
            // } else if (camName.contains("1204")) {
            //     emit pixelSizeInfo(2.2, 2.2);
            // } else if (camName.contains("1.001") && camName.contains("Nano")) {
            //     emit pixelSizeInfo(5.5, 5.5);
            // }
            RayCi_LiveMode_Settings_Units_Spatial_getPixelSizeX(_nDocIdLive, &pixelSizeX);
            RayCi_LiveMode_Settings_Units_Spatial_getPixelSizeY(_nDocIdLive, &pixelSizeY);
            emit pixelSizeInfo(pixelSizeX, pixelSizeY);
        }
    }


void LiveMode::setPowerUnitManually(const bool bPowerUnitManually) const {
    RayCi_LiveMode_Settings_Units_Power_setManually(_nDocIdLive, bPowerUnitManually);
}

void LiveMode::setDivergenceActive(const bool bDivergenceActive) const {
    RayCi_LiveMode_Settings_Units_Divergence_setActive(_nDocIdLive, bDivergenceActive);
}

void LiveMode::startBackgroundCalibration() const {
    RayCi_LiveMode_Background_Recording_start(_nDocIdLive);
}

void LiveMode::stopBackgroundCalibration() const {
    RayCi_LiveMode_Background_Recording_stop(_nDocIdLive);
}

void LiveMode::loadBackground() const {
    RayCi_LiveMode_Background_load(_nDocIdLive);
}

void LiveMode::saveBackground() const {
    RayCi_LiveMode_Background_save(_nDocIdLive);
}

void LiveMode::clearBackground() const {
    RayCi_LiveMode_Background_clear(_nDocIdLive);
}

void LiveMode::getSaturationView() {
    RayCi_LiveMode_Saturation_View_isValid(_nDocIdLive, &bSaturationViewValid);
    if(bSaturationViewValid) {

    }
}

// RayCi_LiveMode_Analysis_Settings_setMethod();
void LiveMode::set2DMethods(const MethodsType method) {
    const auto methodIndex = static_cast<int>(method);
//    RayCi_LiveMode_Analysis_Settings_isMethodAvailable(_nDocIdLive, methodIndex, &bMethodAvailable); // Nano相机bMethodAvailable总是为false,且有两个Method
//    if (bMethodAvailable) {
//        RayCi_LiveMode_Analysis_Settings_setMethod(_nDocIdLive, methodIndex);
//    }
    RayCi_LiveMode_Analysis_Settings_setMethod(_nDocIdLive, methodIndex);
}

//void LiveMode::copyFullImageData(const std::vector<float> &rawData, const std::shared_ptr<CCDImageData<double>> &ImageData) const {
//    for (int j = 0; j < nSizeY; ++j) {
//        for (int i = 0; i < nSizeX; ++i) {
//            ImageData->setValue(j, i, static_cast<double>(rawData[j * nSizeX + i]));
//        }
//    }
//}

void LiveMode::ConnectionToLiveMode() {
    if (_nDocIdLive != 0) {
        int nDocIdLiveSize = 8;
        if (int nDocIdLive[8]; RayCi_LiveMode_list(nDocIdLive, &nDocIdLiveSize)) {
            bool bFound = false;
            for (int i = 0; i < nDocIdLiveSize; ++i) {
                if (nDocIdLive[i] == _nDocIdLive) {
                    bFound = true;
                    break;
                }
            }
            if (!bFound) {
                CloseLiveMode();
            }
        }
    }

    if (_nDocIdLive == 0) {
        int nDocIdLive = 0;
        int nDocIdLiveSize = 1;
        RayCi_LiveMode_list(&nDocIdLive, &nDocIdLiveSize); // 第一次连接LiveMode模式时nDocIdLive和nDocIdLiveSize都为0,下面if的判断会进入else
        if (nDocIdLive != 0) {
            _bUseOpenLive = true;
            _nDocIdLive = nDocIdLive;
        } else {
            _bUseOpenLive = false;
            _nDocIdLive = 0;
            if (!RayCi_LiveMode_open(0, 0, &_nDocIdLive)) { // 尝试连接第一个可用的相机
                cout << "LiveMode could not be opened." << endl;
            }
        }
    }

    if (_nDocIdLive != 0) {
        if (m_vecCameras.empty()) QueryCameraList();
        RayCi_LiveMode_Data_getSizeX(_nDocIdLive, &nSizeX) && RayCi_LiveMode_Data_getSizeY(_nDocIdLive, &nSizeY);
        _startGetNewFrame.reset();
        m_workerThread = std::thread(&LiveMode::Worker_GetNewFrame, this);
        m_queryThread = std::thread(&LiveMode::queryParameter, this);
        RayCi_Callback_register_Document_onClose(&CallbackRayCi_Document_onClose, this, _nDocIdLive, &_nIdCallback_LiveModeClosed);
        RayCi_Callback_register_LiveMode_Data_onNewFrame(&CallbackRayCi_LiveMode_onNewFrame, this, _nDocIdLive, &_nIdCallback_LiveModeNewFrame);
        RayCi_Callback_register_LiveMode_Camera_onCameraRemoved(&CallbackRayCi_LiveMode_onCameraRemoved, this, _nDocIdLive, &_nIdCallback_LiveModeCameraRemoved);

        int nCamIdHigh = 0, nCamIdLow = 0;
        if (RayCi_LiveMode_Camera_getIdCurrentCam(_nDocIdLive, &nCamIdHigh, &nCamIdLow, nullptr, nullptr, nullptr, nullptr)) {
            _dwCamIdLow = nCamIdLow;
            _dwCamIdHigh = nCamIdHigh;
        }

        QueryCameraParameter();
    }
}

int LiveMode::getXSize() {

    if (!m_vecCameras.empty()&&m_vecCameras[0].sName!="") {
        while(nSizeX == 0) {
            RayCi_LiveMode_Data_getSizeX(_nDocIdLive, &nSizeX);

        }
    }
    return nSizeX;
}

int LiveMode::getYSize() {

    if (!m_vecCameras.empty()&&m_vecCameras[0].sName!="") {
        while(nSizeY == 0) {
            RayCi_LiveMode_Data_getSizeY(_nDocIdLive, &nSizeY);
        }
    }
    else {
        if (m_vecCameras.empty())
            emit NoCCDorDog("请连接相机");
        else
            emit NoCCDorDog("Dongle请重新插入");
    }
    return nSizeY;
}

void LiveMode::updateAOIArea(const double x, const double y, const double radiusX, const double radiusY, const double angle,int index) const {
    bool wasEnabled = false;
    RayCi_LiveMode_AOIs_isAoiEnabled(_nDocIdLive, index, &wasEnabled);
    if (!wasEnabled)
        RayCi_LiveMode_AOIs_setAoiEnabled(_nDocIdLive, index, true);
    // RayCi_LiveMode_AOIs_setMainAoi(_nDocIdLive, index);
    RayCi_LiveMode_AOI_setPosition_px(_nDocIdLive, x, y);
    RayCi_LiveMode_AOI_setSize_px(_nDocIdLive, radiusX, radiusY, angle);
}

double LiveMode::getPositionX_px() const {
    double dPositionX_px = 0;
    RayCi_LiveMode_AOI_getPositionX_px(_nDocIdLive, &dPositionX_px);
    return dPositionX_px;
}

double LiveMode::getPositionY_px() const {
    double dPositionY_px = 0;
    RayCi_LiveMode_AOI_getPositionY_px(_nDocIdLive, &dPositionY_px);
    return dPositionY_px;
}

double LiveMode::getMajor_px() const {
    double dMajor_px = 0;
    RayCi_LiveMode_AOI_getMajor_px(_nDocIdLive, &dMajor_px);
    return dMajor_px;
}

double LiveMode::getMinor_px() const {
    double dMinor_px = 0;
    RayCi_LiveMode_AOI_getMinor_px(_nDocIdLive, &dMinor_px);
    return dMinor_px;
}

DWORD LiveMode::Worker_GetNewFrame() {
    while (getNewFrameThreadRunning.load(std::memory_order_acquire)) {
        _startGetNewFrame.waitOne();
        m_nFrameNrRetrieve = QString::number(m_nFrameNrCallback);
        QDateTime currentDateTime = QDateTime::currentDateTime();
        const QString dateTimeString = currentDateTime.toString("yyyy-MM-dd HH:mm:ss");

        const int nDocId = _nDocIdLive;
        char szColorFormat[80];
        int nSize = 80, nBitDepth = 0;
        RayCi_LiveMode_Header_Frame_getColorFormatA(_nDocIdLive, szColorFormat, &nSize); // szColorFormat = "Y8"
        const auto colorFormat = QString(szColorFormat);
        RayCi_LiveMode_Header_Frame_getBitDepth(_nDocIdLive, &nBitDepth); // nBitDepth = 8
        // 获取LiveMode模式下当前图像的宽度和高度
        getXSize();
        getYSize();
        if (RayCi_LiveMode_Data_getSizeX(nDocId, &nSizeX) && RayCi_LiveMode_Data_getSizeY(nDocId, &nSizeY)) {
            if ((nSizeX > 0) && (nSizeY > 0)) {
                emit newFrameNum(m_nFrameNrRetrieve, dateTimeString, nSizeX, nSizeY, colorFormat, nBitDepth);
                bool bSuccessful = false;
                bool bEraseBg = false;
                if (bSuccessful && m_bCropToAoiAvailable && m_bCropToAoi) {
                    int nShape = 0;
                    double dX0 = 0., dY0 = 0., dMajor = 0., dMinor = 0., dAngle = 0.;
                    if (RayCi_LiveMode_AOI_getPositionX_px(nDocId, &dX0) &&
                        RayCi_LiveMode_AOI_getPositionY_px(nDocId, &dY0) &&
                        RayCi_LiveMode_AOI_getMajor_px(nDocId, &dMajor) &&
                        RayCi_LiveMode_AOI_getMinor_px(nDocId, &dMinor) &&
                        RayCi_LiveMode_AOI_getAzimuthAngle_px(0, &dAngle) &&
                        RayCi_LiveMode_AOI_getShape(nDocId, &nShape)) {
                        dAngle *= M_PI / 180.;

                        double dLeft = 0., dTop = 0., dRight = nSizeX - 1, dBottom = nSizeY - 1; // nSizeX = 1280, nSizeY = 1022
                        switch (nShape) {
                            case 1: {
                                // Rectangle
                                const double ca = cos(dAngle);
                                const double sa = sin(dAngle);
                                const double MajorX = 0.5 * ca * dMajor;
                                const double MajorY = 0.5 * sa * dMajor;
                                const double MinorX = -0.5 * sa * dMinor;
                                const double MinorY = 0.5 * ca * dMinor;
                                {
                                    const double x0 = MajorX + MinorX;
                                    const double x1 = MajorX - MinorX;
                                    const double x2 = -MajorX + MinorX;
                                    const double x3 = -MajorX - MinorX;
                                    dLeft = dX0 + min(min(x0, x1), min(x2, x3));
                                    dRight = dX0 + max(max(x0, x1), max(x2, x3));
                                }
                                {
                                    const double y0 = MajorY + MinorY;
                                    const double y1 = MajorY - MinorY;
                                    const double y2 = -MajorY + MinorY;
                                    const double y3 = -MajorY - MinorY;
                                    dTop = dY0 + min(min(y0, y1), min(y2, y3));
                                    dBottom = dY0 + max(max(y0, y1), max(y2, y3));
                                }
                                break;
                            }
                            case 0:
                            default: {
                                // Ellipse, or else
                                const double ca = cos(dAngle);
                                const double sa = sin(dAngle);
                                const double ca2 = ca * ca;
                                const double sa2 = sa * sa;
                                const double csa = ca * sa;
                                const double Major2 = dMajor * dMajor;
                                const double Minor2 = dMinor * dMinor;
                                const double XX = Major2 * ca2 + Minor2 * sa2;         // + 2 * XY * csa;
                                const double XY = csa * (Major2 - Minor2);        // + XY * (ca2 - sa2);
                                const double YY = Major2 * sa2 + Minor2 * ca2;    // - 2 * XY * csa;
                                //const double Disc= XX * YY - XY * XY;
                                const double x12 = sqrt(XX);
                                const double y12 = sqrt(YY);
                                dLeft = dX0 - 0.5 * x12; // 139.5
                                dRight = dX0 + 0.5 * x12; // 1139.5
                                dTop = dY0 - 0.5 * y12; // 10.5
                                dBottom = dY0 + 0.5 * y12; // 1010.5
                                break;
                            }
                        }

                        // round to int
                        int nLeft = static_cast<int>(dLeft); // nLeft = 139
                        int nRight = static_cast<int>(dRight + 1.); // nRight = 1140
                        int nTop = static_cast<int>(dTop); // 10
                        int nBottom = static_cast<int>(dBottom + 1.); // 1011

                        // crop to image
                        nLeft = min(max(nLeft, 0), nSizeX - 1); // 139
                        nRight = min(max(nRight, 0), nSizeX - 1); // 1140
                        nTop = min(max(nTop, 0), nSizeY - 1); // 10
                        nBottom = min(max(nBottom, 0), nSizeY - 1); // 1011
                        int nWidth = nRight - nLeft + 1; // 1002
                        int nHeight = nBottom - nTop + 1; // 1002

//                        std::shared_ptr<CCDImageData<double>> AOIImageData = nullptr;

                        if ((nWidth > 0) && (nHeight > 0)) {
                            int size = nWidth * nHeight; // 1004004
                            bEraseBg = (_sizeData != QSize(nWidth, nHeight)); // true
                            _sizeData.setWidth(nWidth);
                            _sizeData.setHeight(nHeight);
                            vecPreview.resize(size);
                            if (RayCi_LiveMode_Data_getPreviewRect(nDocId, nLeft, nTop, nWidth, nHeight, &vecPreview[0],
                                                                   &size)) {
                                if (size < nWidth * nHeight) break;
                                m_dataFormat = FORMAT_UINT8;
//                                AOIImageData = std::make_shared<CCDImageData<double>>(nWidth, nHeight);
//                                if (AOIImageData != nullptr) {
//                                    try {
////                                        copyFullImageData(vecPreview, AOIImageData);
//                                    }
//                                    catch (...) {
//                                        qDebug() << "get fullImage data error";
//                                        return false;
//                                    }
//                                }
//                                rayCiDataReceiveQueueObserver->put(AOIImageData);
//                                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                                bSuccessful = true;
                            }
                        }
                    }
                }

                if (!bSuccessful) {
                    int size = nSizeX * nSizeY;
                    _sizeData.setWidth(nSizeX);
                    _sizeData.setHeight(nSizeY);
                    if (vecPreview.size() != size) {
                        vecPreview.resize(size);
//                        vecData.resize(size);
                    }
                    if (RayCi_LiveMode_Data_getPreview(nDocId, vecPreview.data(), &size) && RayCi_LiveMode_Data_getFloatField(nDocId, vecData.data(), &size)) {
                        m_dataFormat = FORMAT_UINT8;
                        InitImage(nSizeX, nSizeY);
                        for (int y = 0; y < nSizeY; ++y) {
                            std::memcpy(image.scanLine(y), &vecPreview[y * nSizeX], nSizeX * sizeof(quint8));
                        }
                        currentPixmap = QPixmap::fromImage(image);
                        pixmap = make_shared<QPixmap>(currentPixmap);
//                        floatVec = make_shared<vector<float>>(vecData);

                        // 检查AOI参数是否发生变化并发射更新信号
                        static AOI lastAOI;
                        AOI currentAOI = getPositionSize();

                        // 比较AOI参数是否有变化（考虑浮点数精度）
                        const double epsilon = 1e-6;
                        bool aoiChanged = (abs(currentAOI.dPositionX_px - lastAOI.dPositionX_px) > epsilon ||
                                         abs(currentAOI.dPositionY_px - lastAOI.dPositionY_px) > epsilon ||
                                         abs(currentAOI.dMajor_px - lastAOI.dMajor_px) > epsilon ||
                                         abs(currentAOI.dMinor_px - lastAOI.dMinor_px) > epsilon ||
                                         abs(currentAOI.dAzimuthAngle_px - lastAOI.dAzimuthAngle_px) > epsilon ||
                                         currentAOI.nShape != lastAOI.nShape ||
                                         currentAOI.nMethod != lastAOI.nMethod ||
                                         currentAOI.bActive != lastAOI.bActive);

                        if (aoiChanged) {
                            lastAOI = currentAOI;
                            emit aoiParametersChanged(currentAOI);
                        }

                        emit newFrame();
                    }
                }
            }
        } else {
            _sizeData.setWidth(0);
            _sizeData.setHeight(0);
        }
    }
    return 0;
}




void LiveMode::setAutoContrast(const bool isAuto) {
    isAutoContrast.store(isAuto, std::memory_order_relaxed);
}

void LiveMode::setLUTAutoContrast(const bool isAuto) const {
    RayCi_LiveMode_Settings_Lut_setAutoContrast(_nDocIdLive, isAuto);
}

void LiveMode::InitImage(const int cx, const int cy) {
    if (image.isNull() || image.width() != cx || image.height() != cy) {
        image = QImage(cx, cy, QImage::Format_Indexed8);
    }
    isAutoContrast.load(std::memory_order_relaxed) ? getActualLut(LUT, 8) : getBaseLut(LUT);
    QVector<QRgb> colorTable(256);
    for (int i = 0; i < 256; ++i) {
        colorTable[i] = qRgba(LUT[i][0], LUT[i][1], LUT[i][2], LUT[i][3]);
    }
    image.setColorTable(colorTable);
}

void LiveMode::CallbackRayCi_Document_onClose(IN void *pContext, IN int nIdDoc) {
    if (auto *pThis = static_cast<LiveMode *>(pContext)) {
        pThis->CloseLiveMode();
    }
}

void LiveMode::CallbackRayCi_LiveMode_onNewFrame(IN void *pContext, IN int nIdDoc, IN const int nFrameNr, IN const tm *tmFrameTime, IN int nFrameTime_ms) {
    auto *pThis = static_cast<LiveMode *>(pContext);
    pThis->m_nFrameNrCallback = nFrameNr;
    pThis->_startGetNewFrame.set();
}

void LiveMode::CallbackRayCi_LiveMode_onCameraRemoved(IN void *pContext, IN int nIdDoc, IN int nIdCamHigh, IN int nIdCamLow) {

}

void LiveMode::CloseLiveMode() {
    if (_nIdCallback_LiveModeCameraRemoved != 0) {
        RayCi_Callback_unregisterById(_nIdCallback_LiveModeCameraRemoved);
        _nIdCallback_LiveModeCameraRemoved = 0;
    }
    if (_nIdCallback_LiveModeClosed != 0) {
        // both methods are possible
        RayCi_Callback_unregisterById(_nIdCallback_LiveModeClosed);
        _nIdCallback_LiveModeClosed = 0;
    }
    if (_nIdCallback_LiveModeNewFrame != 0) {
        RayCi_Callback_unregisterById(_nIdCallback_LiveModeNewFrame);
        _nIdCallback_LiveModeNewFrame = 0;
    }
    _startGetNewFrame.set();
    getNewFrameThreadRunning.store(false, std::memory_order_release);
    queryParameterThreadRunning.store(false, std::memory_order_release);

    if (m_workerThread.joinable()) {
        m_workerThread.join();
    }
    if (m_queryThread.joinable()) {
        m_queryThread.join();
    }
    if (_nDocIdLive != 0) {
        if (!_bUseOpenLive)
            RayCi_LiveMode_close(_nDocIdLive, true);
        _bUseOpenLive = false;
        _nDocIdLive = 0;
    }
    if (_nDocIdMeasure != 0) {
        RayCi_closeDocument(_nDocIdMeasure, true);
    }
    _dwCamIdLow = 0;
    _dwCamIdHigh = 0;
}

void LiveMode::setExposureTime(const int index) const {
    RayCi_LiveMode_Camera_ExposureTime_setExposureTime(_nDocIdLive, _vecExposureTimes[index]);
}


void LiveMode::setGain(const int index) const {
    RayCi_LiveMode_Camera_Gain_setGain(_nDocIdLive, _vecGain[index]);
}


void LiveMode::setBinning(const int index) const {
    RayCi_LiveMode_Camera_Binning_setBinning(_nDocIdLive, index);
}



void LiveMode::setAutoExposureTime(const bool autoExposure) const {
    RayCi_LiveMode_Camera_ExposureTime_setAutomatic(_nDocIdLive, autoExposure);
//    RayCi_LiveMode_Camera_ExposureTime_getExposureTime(_nDocIdLive, &exTime);
}

void LiveMode::setAutoGain(const bool autoGain) const {
    RayCi_LiveMode_Camera_Gain_setAutomatic(_nDocIdLive, autoGain);
}

void LiveMode::setReducePixelClock(const bool reducePixelClock) const {
    RayCi_LiveMode_Camera_PixelClock_setReduce(_nDocIdLive, reducePixelClock);
}

double LiveMode::getExposureTime() const {
    double exTime = 0;
    RayCi_LiveMode_Camera_ExposureTime_getExposureTime(_nDocIdLive, &exTime);
    return exTime;
}

int LiveMode::getGain()  {
    double gain =0;
    RayCi_LiveMode_Camera_Gain_getGain(_nDocIdLive, &gain);
    int  index= findGainIndex(_vecGain,gain);
    return index;
}

int LiveMode::getBinning() {
    int binning = 0;
    RayCi_LiveMode_Camera_Binning_getBinning(_nDocIdLive, &binning);
    return binning;
}


double LiveMode::getMaxFrameRate() const {
    double max = 0;
    RayCi_LiveMode_Camera_FrameRate_getMaximum(_nDocIdLive, &max);
    return max;
}

double LiveMode::getMinFrameRate() const {
    double min = 0;
    RayCi_LiveMode_Camera_FrameRate_getMinimum(_nDocIdLive, &min);
    return min;
}

double LiveMode::getFrameRate() const {
    double rate = 0;
    RayCi_LiveMode_Camera_FrameRate_getFrameRate(_nDocIdLive, &rate);
    return rate;
}



void LiveMode::AdjustAoiToCenter(bool check) {
     RayCi_LiveMode_AOI_Adjustment_setActive  (_nDocIdLive,check);
}

void LiveMode::queryControl() {
    bool isExposureTimeAutomatic = false;
    RayCi_LiveMode_Camera_ExposureTime_isAutomatic(_nDocIdLive, &isExposureTimeAutomatic);
    std::cout << "isExposureTimeAutomatic = " << isExposureTimeAutomatic << std::endl;
    emit updateControl(isExposureTimeAutomatic);
}

void LiveMode::QueryCameraParameter() {
    int nListSize = 0;
    _vecExposureTimes.clear();
    if (RayCi_LiveMode_Camera_ExposureTime_getList(_nDocIdLive, nullptr, &nListSize) &&
        (nListSize > 0)) // nListSize = 19  1
    {
        _vecExposureTimes.resize(nListSize); // 19
        RayCi_LiveMode_Camera_ExposureTime_getList(_nDocIdLive, &_vecExposureTimes.front(), &nListSize);

        emit newCamExposureTimeList(_vecExposureTimes);
    }
    auto  exposureTime =  getExposureTime();
    auto index = findExposureTimeIndex(_vecExposureTimes, exposureTime);
    if (index != -1) {
        emit newOptionsExposurement(index);
    }
    nListSize = 0;
    _vecGain.clear();
    if (RayCi_LiveMode_Camera_Gain_getList(_nDocIdLive, nullptr, &nListSize) && (nListSize > 0)) {
        _vecGain.resize(nListSize); // nListSize = 1
        RayCi_LiveMode_Camera_Gain_getList(_nDocIdLive, &_vecGain.front(), &nListSize);
    }
     RayCi_LiveMode_Camera_Gain_getGain(_nDocIdLive, &dGain); // dGain = 1
     dGainIndex = findGainIndex(_vecGain, dGain);
    RayCi_LiveMode_Camera_Binning_getBinning(_nDocIdLive, &nBinning); // nBinning = 0
        emit newOptionsGainAndBinning(dGainIndex, nBinning);
    // RayCi Selection
    char szAccessory[80];
    int nSize = 80, nSumming = 0;
    RayCi_LiveMode_Camera_Accessory_getAccessoryA(_nDocIdLive, szAccessory, &nSize); // szAccessory = "plain"
    const auto accessory = QString(szAccessory);
    double dFloatingAverage = 0;
    RayCi_LiveMode_Camera_getFloatingAverage(_nDocIdLive, &dFloatingAverage); // dFloatingAverage = 0
    RayCi_LiveMode_Camera_getSumming(_nDocIdLive, &nSumming); // nSumming = 1
    emit newCamSelectionInfo(accessory, dFloatingAverage, nSumming);

    RayCi_LiveMode_Settings_Units_Spatial_getPixelSizeX(_nDocIdLive, &dPixelSizeX);
    RayCi_LiveMode_Settings_Units_Spatial_getPixelSizeY(_nDocIdLive, &dPixelSizeY);
    emit pixelSizeInfo(dPixelSizeX, dPixelSizeY);
//    double dMaximumIntensity = 0, dMinimumIntensity = 0;
//    RayCi_LiveMode_Settings_IntensityRange_Maximum_getMaximum(_nDocIdLive, &dMaximumIntensity); // 1
//    RayCi_LiveMode_Settings_IntensityRange_Minimum_getMinimum(_nDocIdLive, &dMinimumIntensity); // 0
//    int iListSize = 0;
//    RayCi_LiveMode_Camera_Accessory_getListSize(_nDocIdLive, &iListSize);
//    std::vector<QString> vecList;
//    char szAccessoryItem[80];
//    int nSize = _countof(szAccessoryItem);
//    for (int i = 0; i < iListSize; i++) {
//        RayCi_LiveMode_Camera_Accessory_getListItemA(_nDocIdLive, i, szAccessoryItem, &nSize);
//    }
}

void LiveMode::queryParameter() {
    while (queryParameterThreadRunning.load(std::memory_order_acquire)) {
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        // Header Frame
//        RayCi_LiveMode_Header_Frame_getColorFormatA(_nDocIdLive, szColorFormat, &nSize); // szColorFormat = "Y8"
//        RayCi_LiveMode_Header_Frame_getBitDepth(_nDocIdLive, &nBitDepth); // nBitDepth = 8
        RayCi_LiveMode_Header_Camera_getTemperature(_nDocIdLive, &dTemperature); // dTemperature = 0

        // Header LaserI
        RayCi_LiveMode_Header_Laser_getWaveLength(_nDocIdLive, &dWaveLength);

        // RayCi Control
        RayCi_LiveMode_Camera_ExposureTime_getExposureTime(_nDocIdLive,
                                                           &dExposureTime); // dExposureTime = 139.98650000000001
        int nowExposureTimeIndex=-1;
        nowExposureTimeIndex = findExposureTimeIndex(_vecExposureTimes, dExposureTime);
        if (nowExposureTimeIndex!=dExposureTimeindex){
        emit newOptionsExposurement(nowExposureTimeIndex);
            }
        dExposureTimeindex=nowExposureTimeIndex;
        RayCi_LiveMode_Camera_ExposureTime_isAvailable(_nDocIdLive, &isExposureTimeAvailable);
        RayCi_LiveMode_Camera_ExposureTime_isAutomatic(_nDocIdLive, &isExposureTimeAutomatic);

        RayCi_LiveMode_Camera_Binning_getBinning(_nDocIdLive, &nBinning); // nBinning = 0
        int nowGainIndex=-1;
        RayCi_LiveMode_Camera_Gain_getGain(_nDocIdLive, &dGain); // dGain = 1

         nowGainIndex = findGainIndex(_vecGain, dGain);
         if ((nowGainIndex!=dGainIndex)||(prevBinning!=nBinning)) {
             emit newOptionsGainAndBinning(nowGainIndex, nBinning);
         }
         dGainIndex=nowGainIndex;
         prevBinning=nBinning;
        RayCi_LiveMode_Camera_Gain_isAvailable(_nDocIdLive, &isGainAvailable);
        RayCi_LiveMode_Camera_Gain_isAutomatic(_nDocIdLive, &isGainAutomatic);

//        RayCi_LiveMode_Camera_Binning_isAvailable(_nDocIdLive,&binningAvailable);

        RayCi_LiveMode_Camera_PixelClock_isReduced(_nDocIdLive, &pixelClockIsReduced);
        RayCi_LiveMode_Camera_PixelClock_isAvailable(_nDocIdLive, &pixelClockIsAvailable);
        RayCi_LiveMode_Camera_FrameRate_getFrameRate(_nDocIdLive, &dFrameRate);
        RayCi_LiveMode_Camera_FrameRate_getMaximum(_nDocIdLive, &dMaximum); // dMaximum = 7.1435440000000003
        RayCi_LiveMode_Camera_FrameRate_getMinimum(_nDocIdLive, &dMinimum); // dMinimum = 6.9086819999999998
        RayCi_LiveMode_Camera_Attenuator_isAvailable(_nDocIdLive,
                                                     &bAttenuatorAvailable); // bAttenuatorAvailable = false

//        RayCi_LiveMode_Settings_Units_Spatial_isManually(_nDocIdLive, &bSpatialManually); // bSpatialManually = false
        RayCi_LiveMode_Settings_Units_Spatial_getPixelSizeX(_nDocIdLive, &dPixelSizeX);
        RayCi_LiveMode_Settings_Units_Spatial_getPixelSizeY(_nDocIdLive, &dPixelSizeY);
        int nSize = 80;
        RayCi_LiveMode_Settings_Units_Spatial_getUnitA(_nDocIdLive, szUnit, &nSize); // szUnit = "mm"
        RayCi_LiveMode_Settings_Units_Power_isManually(_nDocIdLive, &bPowerManually);
        RayCi_LiveMode_Settings_Units_Power_getPower(_nDocIdLive, &dUnitPower);
        RayCi_LiveMode_Settings_Units_Power_getUnitA(_nDocIdLive, szPUnit, &nSize); // szPUnit = ""

        // Corrections Background
        RayCi_LiveMode_Background_Recording_Settings_getFrameSpan(_nDocIdLive, &nFrameSpan); // nFrameSpan = 16
        RayCi_LiveMode_Background_Recording_Settings_getTimeSpan(_nDocIdLive, &dTimeSpan); // dTimeSpan = 5
        RayCi_LiveMode_Background_Recording_Settings_isAllExposure(_nDocIdLive, &bisAllExposure); // bisAllExposure = true
        RayCi_LiveMode_Background_Recording_Settings_isCurrentExposure(_nDocIdLive, &bisCurrentExposure); // bisCurrentExposure = false

        emit newCamSetting(this);
    }
}

bool LiveMode::IsPortAvailable(const WORD wPort) {
    DWORD dwSize = 0;
    GetTcpTable(nullptr, &dwSize, true);
    if (const auto pTcpTable = static_cast<PMIB_TCPTABLE>(_alloca(dwSize)); GetTcpTable(pTcpTable, &dwSize, true) == NO_ERROR) {
        for (DWORD i = 0; i < pTcpTable->dwNumEntries; ++i) {
            if (const MIB_TCPROW & entry = pTcpTable->table[i];
                (entry.dwState == MIB_TCP_STATE_LISTEN) && (ntohs(static_cast<WORD>(entry.dwLocalPort)) == wPort)) {
                return false;
            }
        }
    }
    return true;
}

int LiveMode::findExposureTimeIndex(const std::vector<double>& exposureTimes, double targetExposure) {
    const double epsilon = 0.2;
    auto it = std::find_if(exposureTimes.begin(), exposureTimes.end(),
                          [targetExposure, epsilon](double value) {
                              return std::abs(value - targetExposure) < epsilon;
                          });
    if (it != exposureTimes.end()) {
        return static_cast<int>(std::distance(exposureTimes.begin(), it));
    } else {
        return -1;
    }
}

int LiveMode::findGainIndex(const std::vector<double>& gains, double targetGain) {
    const double epsilon = 1e-3;
    auto it = std::find_if(gains.begin(), gains.end(),
                          [targetGain, epsilon](double value) {
                              return std::abs(value - targetGain) < epsilon;
                              });
    if (it != gains.end()) {
        return static_cast<int>(std::distance(gains.begin(), it));
        } else {
                return -1;
                }

}

void LiveMode::QueryCameraList() {
    m_vecCameras.clear();
    int nCamListSize = 0;
    if (RayCi_Cameras_getCount(&nCamListSize)) // nCamListSize = 1
    {
        m_bCropToAoiAvailable = true;
        for (int nItem = 0; nItem < nCamListSize; ++nItem) {
            int nCamIdHigh = 0, nCamIdLow = 0; // nCamIdHigh = 40, nCamIdLow = -191205103
            char szCamName[80];  // "CinCam CMOS 1201 IR"
            int nNameSize = sizeof(szCamName); // RayCi_Cameras_list
            if (RayCi_Cameras_getIdCamHigh(nItem, &nCamIdHigh) && RayCi_Cameras_getIdCamLow(nItem, &nCamIdLow) &&
                RayCi_Cameras_getNameA(nItem, szCamName, &nNameSize) &&
                RayCi_Cameras_getRemarkA(nItem, nullptr, nullptr)) {
                char szCamSerial[80];
                RayCi_Cameras_getSerialNumberA(nItem, szCamSerial, &nNameSize);
                m_vecCameras.emplace_back(szCamName, szCamSerial, nCamIdLow, nCamIdHigh);
                cameraName = szCamName;
            }
        }
        emit newCamInfo(m_vecCameras);

    }
}

QString LiveMode::OnUpdateSize() const {
    int nBitsPerPixel = 0;
    QString szDataFormat;
    switch (m_dataFormat) {
        case FORMAT_UINT8:
            nBitsPerPixel = 8;
            szDataFormat = "byte";
            break;
        case FORMAT_FLOAT:
            nBitsPerPixel = 32;
            szDataFormat = "float";
            break;
    }

    QString str = QString("%1 x %2 x %3 bpp (%4)")
            .arg(m_sizeData.width())
            .arg(m_sizeData.height())
            .arg(nBitsPerPixel)
            .arg(szDataFormat);
    return str;
}

//QString LiveMode::OnUpdateFps() {
//    QString str;
//    if (m_timeFps.time() > 0) {
//        str = QString("fps: %1").arg(1000.0 / m_timeFps.time(), 0, 'f', 2);
//    } else {
//        str = "fps: -";
//    }
//    // fpsLabel->setText(str);
//    m_timeFps.start(); // 重置计时器
//    return str;
//}

QString LiveMode::OnUpdateTimeQuery() const {
    QString str = QString("Query: %1 ms").arg(m_timeDataQuery.time(), 0, 'f', 1);
    return str;
}

QString LiveMode::OnUpdateTimeConvert() const {
    QString str = QString("Convert: %1 ms").arg(m_timeDataConvert.time(), 0, 'f', 1);
    return str;
}

void LiveMode::OnBnClickedCheckCropToAoi() {
    if (m_bCropToAoiAvailable) {
        m_bCropToAoi = !m_bCropToAoi;
//        m_evStartGetNewFrame.SetEvent();		// re-capture frame
    }
}




void LiveMode::showOrHideAOI(int index,bool show) const {
    bool a;
    a=RayCi_LiveMode_AOIs_setAoiEnabled(_nDocIdLive, index, show);
    qDebug()<< "AOI" << index << "set" << show << "result" << a;
}


void LiveMode::setMainAOI(int index) {
    // 检查索引是否在有效范围内
    int maxAOICount=0;
    RayCi_LiveMode_AOIs_getMaxCount(_nDocIdLive,&maxAOICount);
    qDebug()<< "AOI count" << maxAOICount;
    if (index < 0 || index >= maxAOICount) {
        if (!m_vecCameras.empty() )
        throw std::out_of_range("AOI index out of range");
    }

    // 查询当前 AOI 的启用状态
    bool isEnabled=false;
    RayCi_LiveMode_AOIs_isAoiEnabled(_nDocIdLive, index,&isEnabled);
    qDebug() << "AOI" << index << "isEnabled" << isEnabled;

    // 若未启用则启用
    if (!isEnabled) {
        showOrHideAOI(index, true);
        qDebug() << "AOI" << index << "enabled";
    }

    // 设置为主 AOI
    RayCi_LiveMode_AOIs_setMainAoi(_nDocIdLive, index);
    qDebug() << "AOI" << index << "set as main AOI";

}